#include <SDL/SDL.h>
#include <SDL/SDL_image.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glext.h>
#include "BM_anitex.h"
#include "TGA/tga.h"


BMTex::BMTex(){
}

extern void logMessage(const char *message, ...);

/*int BMTex::loadPCX(char filename[128]){

	SDL_Surface *TextureImage[1];

    if ((TextureImage[0] = IMG_Load_RW(SDL_RWFromFile(filename, "rb"), 1))){

		//glGenTextures(1, &tex);
	    glBindTexture(GL_TEXTURE_2D, tex);

	    if(TextureImage[0]->format->BitsPerPixel==8) {
            glTexImage2D(GL_TEXTURE_2D,0,GL_RGB8,TextureImage[0]->w,TextureImage[0]->h,0,GL_COLOR_INDEX,GL_UNSIGNED_BYTE, TextureImage[0]->pixels);
        }
        else
            return 0;

        // Specify filtering and edge actions// Specify filtering and edge actions
        glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP);
        glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP);
    }else{
		cout << "Failed to load %s | Error: %s" << filename << IMG_GetError() << std::endl;
		return 0;
	}

	if(tex)
        printf("Successful load %s",filename);

    BPP = TextureImage[0]->format->BitsPerPixel;
    Height = TextureImage[0]->h;
    Width = TextureImage[0]->w;
    pixels = TextureImage[0]->pixels;

    if (TextureImage[0]) {
	    SDL_FreeSurface( TextureImage[0] );
    }
	return 1;
}*/

/* Will modify the image array so it is vertically
flipped.
 * This is intened to be a helper function to comply
with
 * OpenGL's y-coordinate system.
 * Returns -1 on failure, 0 on success
 * Warning: The original data will be modified.
 * Sample Usage: invert_image(image->pitch, image->h,
&image->pixels)
  */


//from http://lists.libsdl.org/pipermail/sdl-libsdl.org/2002-September/030534.html
static int invert_image(int pitch, int height, void* image_pixels)
{
    int index;
    void* temp_row;
    int height_div_2;

    temp_row = (void *)malloc(pitch);
    if(NULL == temp_row)
    {
        SDL_SetError("Not enough memory for image inversion");
        return -1;
    }
    /* if height is odd, don't need to swap middle row */
    height_div_2 = (int) (height * .5);
    for(index = 0; index < height_div_2; index++) 	{
        /* uses string.h */
        memcpy((Uint8 *)temp_row, (Uint8 *)(image_pixels) + pitch * index, pitch);

        memcpy((Uint8 *)(image_pixels) + pitch * index, (Uint8 *)(image_pixels) + pitch * (height - index-1), pitch);
        memcpy((Uint8 *)(image_pixels) +pitch * (height - index-1),temp_row,pitch);
    }
    free(temp_row);
    return 0;
}

/* Convenience wrapper for invert_image */
int SDL_InvertSurfaceAniTex(SDL_Surface* image)
{
    if(NULL == image)
    {
        SDL_SetError("Surface is NULL");
        return -1;
    }
    return( invert_image(image->pitch, image->h, image->pixels) );
}


int BMTex::loadBMP(char filename[128], bool is_skybox)
{
    if (is_skybox) {
        cout << "\n" << filename;
        //exit(0);
    }
    SDL_Surface *TextureImage[1];
    if ( ( TextureImage[0] = SDL_LoadBMP(filename))){
        SDL_InvertSurfaceAniTex(TextureImage[0]);
		glGenTextures(1, &tex);
		if (is_skybox)
            glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
	    glBindTexture(GL_TEXTURE_2D, tex);
	    if (!is_skybox) {
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
            glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage[0]->w, TextureImage[0]->h, 0, GL_BGR, GL_UNSIGNED_BYTE, TextureImage[0]->pixels);
        }
        else {
            // build mipmaps
            gluBuild2DMipmaps(GL_TEXTURE_2D, 3, TextureImage[0]->w, TextureImage[0]->h, GL_BGR, GL_UNSIGNED_BYTE, TextureImage[0]->pixels);

            /*
                Tell OpenGL the quality of the texture map.
                GL_LINEAR_MIPMAP_LINEAR is te smoother but GL_LINEAR_MIPMAP_NEAREST is faster (better for slower computers) although it looks pixilated.
            */

            glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_NEAREST);
            glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);

            /*
                The default GL_TEXTURE_WRAP_S and GL_TEXTURE_WRAP_WRAP_T property is GL_REPEAT.
                We must use GL_CLAMP_TO_EDGE because otherwise it makes te skybox to look weird (GL_CLAMP_TO_EDGE doesn't repeat when bound to an object)
            */
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
        }
    }else{
		printf("!load %s\n",filename);
		return 0;
	}
	if(tex)
	printf("loaded %s\n",filename);
    if ( TextureImage[0] )
	    SDL_FreeSurface( TextureImage[0] );
	return 1;
}

int BMTex::loadTGA(char filename[128], bool is_skybox)
{
    TGAImg Img; // Image loader

    // Load our Texture
    if(Img.Load(filename)!=IMG_OK)
        return 0;

    glGenTextures(1, &tex);
    glBindTexture(GL_TEXTURE_2D,tex); // Set our Tex handle as current

    // Create the texture
    if(Img.GetBPP()==24)
        glTexImage2D(GL_TEXTURE_2D,0,3,Img.GetWidth(),Img.GetHeight(),0,GL_RGB,GL_UNSIGNED_BYTE,Img.GetImg());
    else if(Img.GetBPP()==32)
        glTexImage2D(GL_TEXTURE_2D,0,4,Img.GetWidth(),Img.GetHeight(),0,GL_RGBA,GL_UNSIGNED_BYTE,Img.GetImg());
    else
        return 0;

    // Specify filtering and edge actions
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP);

    Img.~TGAImg();

    return 1;
}

int BMTex::load(char filename[128], bool is_skybox){
	// Check extension
	if(strstr(strlwr(strdup(filename)), ".bmp"))
		return loadBMP(filename, is_skybox);
	else if(strstr(strlwr(strdup(filename)), ".tga"))
			return loadTGA(filename, is_skybox);
    else {
        printf("Error loading texture file: %s\n", filename);
        printf("Unsupported texture file format ! Only .BMP, .TGA !\n\n");
    }

    return 0;
}

GLuint BMTex::getTex(){
	return tex;
}

void BMTex::freeTex(){
	free(&tex);
}
/*******************************************************************************************/
/////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////

int BMAniTex::load(int t, std::string foldername){
	char str[256];
	if(t <= MAX_ANITEX){
		for(int i=t-1; i >= 0; i--){
			sprintf(str,"%s/%d.bmp",foldername.c_str(),i+1);
		    if(!tex[i].load(str))
				return 0;
			setDelay(i,1);
		}
	}else{
		return 0;
	}
	setLoop(-1);
	loaded = 1;
	total = t;
	return 1;
}


/*******************************************************************************/
void BMAniTex::freeTex(){
	for(int i=0; i < total; i++) tex[i].freeTex();
}



/*******************************************************************************/
void BMAniTex::updateTex(){
	if(loop == -1 || cur_loop < loop){
		if( cur_delay <= delay[cur_tex]){
			cur_delay ++;
		}else{
			if(cur_tex < total - 1){
				cur_tex ++;
			}else{
				cur_tex = 0;
				cur_loop ++;
			}
			cur_delay = 0;
		}
	}
}


/*******************************************************************************/
BMAniTex::BMAniTex(){ loaded = 0; total = 0; cur_delay = 0; cur_tex = 0; cur_loop = 0; for(int i = 0; i < MAX_ANITEX; i++) delay[i] = 0;}


/*******************************************************************************/
void  BMAniTex::setTotal(int t){
    total = t;
}

/*******************************************************************************/
void  BMAniTex::setLoop(int l){
    loop = l;
}

/*******************************************************************************/
void  BMAniTex::setDelay(int num, int de){
     delay[num] = de;
}

/*******************************************************************************/
void  BMAniTex::setCurDelay( int de){
    cur_delay = de;
}

/*******************************************************************************/
void  BMAniTex::setCurTex( int im){
    cur_tex = im;
}

/*******************************************************************************/
void  BMAniTex::setCurLoop(int l){
     cur_loop = l;
}

/*******************************************************************************/
int  BMAniTex::getTotal(){
    return total;
}

/*******************************************************************************/
int  BMAniTex::getLoop(){
    return loop;
}


/*******************************************************************************/
GLuint BMAniTex::getTex(int num ){
        return tex[num].getTex();
}

/*******************************************************************************/
int  BMAniTex::getDelay(int num){
    return delay[num];
}

/*******************************************************************************/
int  BMAniTex::getCurDelay(){
    return cur_delay;
}

/*******************************************************************************/
int  BMAniTex::getCurTex(){
    return cur_tex;
}

/*******************************************************************************/
int  BMAniTex::getCurLoop(){
     return cur_loop;
}
